/*********************************
 * jsmud: JavaScript MUD Kernel  *
 *          Mojodo, Inc.         *
 *        Sean Middleditch       *
 * Copyright (C) 2005,2007,2008  *
 *      All rights reserved.     *
 *   See COPYING for licensing.  *
 *********************************/

/* ==== SYSTEM INCLUDES ==== */

#include <string.h>

/* ==== LIBRARY INCLUDES ==== */

#include <sqlite3.h>
#include <zlib.h>

#define XP_UNIX 1
#define MOZILLA_1_8_BRANCH 1
#include <jsapi.h>
#undef XP_UNIX
#undef MOZILLA_1_8_BRANCH

/* ==== JSMUD HEADER ==== */

#include "jsmud.h"

/* ==== GLOBALS ==== */

sqlite3* db_context = NULL;

/* ==== FUNCTIONS ==== */

sqlite3_stmt* db_prepare (const char* sql, uintN argc, jsval* argv)
{
	sqlite3_stmt* stmt;
	uintN i;

	/* prepare the statement */
	if (sqlite3_prepare(db_context, sql, strlen(sql), &stmt, NULL) != SQLITE_OK) {
		JS_ReportError(js_context, "sqlite3_prepare() failed: %s", sqlite3_errmsg(db_context));
		return NULL;
	}

	/* bind all the script arguments to the SQL statement */
	for (i = 0; i < argc; ++i) {
		/* user passed in a number, bind as double */
		if (JSVAL_IS_NUMBER(argv[i])) {
			double db;
			JS_ValueToNumber(js_context, argv[i], &db);
			if (sqlite3_bind_double(stmt, i + 1, db) != SQLITE_OK) {
				JS_ReportError(js_context, "sqlite3_bind_double() failed: %s", sqlite3_errmsg(db_context));
				sqlite3_finalize(stmt);
				return NULL;
			}
		/* user passed in an integer, bind as a 32-bit int */
		} else if (JSVAL_IS_INT(argv[i])) {
			intN in;
			JS_ValueToInt32(js_context, argv[i], &in);
			if (sqlite3_bind_int(stmt, i + 1, in) != SQLITE_OK) {
				JS_ReportError(js_context, "sqlite3_bind_int() failed: %s", sqlite3_errmsg(db_context));
				sqlite3_finalize(stmt);
				return NULL;
			}
		/* user passed in something else, convert to string and bind */
		} else {
			JSString* str;
			const char* bytes;
			if ((str = JS_ValueToString(js_context, argv[i])) == NULL) {
				JS_ReportError(js_context, "Unconvertible data type passed to SQL");
				sqlite3_finalize(stmt);
				return NULL;
			}
			bytes = JS_GetStringBytes(str);
			if (sqlite3_bind_text(stmt, i + 1, bytes, strlen(bytes), SQLITE_TRANSIENT) != SQLITE_OK) {
				JS_ReportError(js_context, "sqlite3_bind_int() failed: %s", sqlite3_errmsg(db_context));
				sqlite3_finalize(stmt);
				return NULL;
			}
		}
	}

	return stmt;
}

JSBool db_get_result (sqlite3_stmt* stmt, jsval* retval)
{
	int err;
	JSString* string;
	JSObject* result;
	int i;
	int size;
	jsval value;

	/* increment result set */
	err = sqlite3_step(stmt);

	/* all done, no more rows to be returned */
	if (err == SQLITE_DONE) {
		return JS_FALSE;

	/* a row - create result object */
	} else if (err == SQLITE_ROW) {
		/* create object */
		result = JS_NewObject(js_context, NULL, NULL, NULL);
		if (result == NULL)
			return JS_FALSE;
		*retval = OBJECT_TO_JSVAL(result);

		/* iterate over columns and set result object properties */
		size = sqlite3_column_count(stmt);
		for (i = 0; i < size; ++i) {
			/* convert result into jsval */
			switch(sqlite3_column_type(stmt, i)) {
				case SQLITE_INTEGER:
					value = INT_TO_JSVAL(sqlite3_column_int(stmt, i));
					break;
				case SQLITE_FLOAT:
					if (JS_NewDoubleValue(js_context, sqlite3_column_double(stmt, i), &value) == JS_FALSE)
						return JS_FALSE;
					break;
				case SQLITE_TEXT:
					if ((string = JS_NewStringCopyZ(js_context, (char*)sqlite3_column_text(stmt, i))) == NULL)
						return JS_FALSE;
					value = STRING_TO_JSVAL(string);
					break;
				default:
					value = JSVAL_NULL;
			}
			
			/* bind property */
			if (JS_SetProperty(js_context, result, sqlite3_column_name(stmt, i), &value) == JS_FALSE)
				return JS_FALSE;
		}

		return JS_TRUE;

	/* some other error occured */
	} else {
		JS_ReportError(js_context, "sqlite3_step() failed: %s", sqlite3_errmsg(db_context));
		return JS_FALSE;
	}
}

/* vim: set tabstop=2 shiftwidth=2 ai: */
